#include "include/CHttpClient.h"
#include "include/CGetHostByName.h"



BASENET_BEGIN_NAMESPACE

// 客服端回调函数
static void httpClientCB(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
	CHttpClient *p = (CHttpClient *)fn_data;
	if(p) {
		p->__httpClientCB(c,ev,ev_data);
	}
}

// 连接超时
static void httpConTimeOutCB(void *arg)
{
	CHttpClient *p = (CHttpClient *)arg;
	if(p) {
		p->__httpConTimeOutCB();
	}

}

//构造函数
CHttpClient::CHttpClient()
{
	m_lPrev_second = 0;
	m_sMethod = GET;
	m_bRunStatus = false;
	m_iConTime = 3;
	m_iConTimeTmp = 0;
	m_iRecvTime = 5;
	m_sCon = NULL;
	m_tBody = "";
	m_pHeadParams = NULL;
	m_pParms = NULL;;
	memset(&m_sTlsOpts,0x00,sizeof(struct mg_tls_opts));
	mg_mgr_init(&m_sMgr);
	CGetHostByName::initMongoose();
}

// 析构函数
CHttpClient::~CHttpClient()
{
	mg_mgr_free(&m_sMgr);
}

const char *CHttpClient::getHttpMethod()
{
	switch(m_sMethod){
		case GET:
			return "GET";
		case POST:
			return "POST";
	}
	return "GET";
}

// 发送请求数据
void CHttpClient::sendReuireData(struct mg_connection *c, int ev, void *ev_data)
{
	CMultipartParser parser;
	string extra_headers="Pragma: no-cache\r\nCache-Control: no-cache\r\n";
	// post提交参数对只能使用multipart/form-data提交
	if(m_sMethod == POST && ((m_pParms && m_pParms->size()!=0) || !m_sFileName.empty()))
	{
		extra_headers +="Content-Type: multipart/form-data; boundary="+parser.boundary()+"\r\n";
	}

	// 头组装
	if(m_pHeadParams) {
		for (MAP_PARAMS::iterator itr = m_pHeadParams->begin(); itr!=  m_pHeadParams->end(); ++itr) {
			extra_headers += itr->first + ":" + itr->second + "\r\n";
		}
	}

	// 如果有文件需要提交
	string parserContent = "";
	if(m_sMethod == POST && !m_sFileName.empty()){
		parser.addFile(m_sFileName,m_sFilePath);
		parserContent = parser.genBodyContent();
	}
	// 请求路径
	string path = mg_url_uri(m_sUrl.c_str());
	if(m_pParms) {
		for (MAP_PARAMS::iterator itr =  m_pParms->begin(); itr!=  m_pParms->end();) {
				switch(m_sMethod){
				case POST:
					if(m_tBody.empty()){
						parser.addParameter(itr->first, itr->second);
						if(++itr == m_pParms->end()) {
							parserContent = parser.genBodyContent();
						}
					}
					break;
				case GET:
					if(itr == m_pParms->begin()){
						path +="?";
					}
					path += (itr->first + "=" + itr->second);
					if(++itr != m_pParms->end()) {
						path += "&";
					}
				break;
			}
		}
	}

	// 请求内容(body内容优先)
	string body = !m_tBody.empty() ? m_tBody : parserContent;

	// 主机名字
	struct mg_str host = mg_url_host(m_sUrl.c_str());
	unsigned short port = mg_url_port(m_sUrl.c_str());

	if(port > 0 && port != 80 && port != 443 && port != 1883 && port != 8883){
		mg_printf(c,"%s %s HTTP/1.1\r\n"
					"Host: %.*s:%d\r\n"
					"Content-Length:%d\r\n"
					"%s\r\n"
					"%s",
					getHttpMethod(),path.c_str(),
					host.len,host.ptr,port,
					body.length(),
					extra_headers.c_str(),
					body.c_str()
				    );
		return;
	}
	mg_printf(c,"%s %s HTTP/1.1\r\n"
				"Host: %.*s\r\n"
				"Content-Length:%d\r\n"
				"%s\r\n"
				"%s",
				getHttpMethod(),path.c_str(),
				host.len,host.ptr,
				body.length(),
				extra_headers.c_str(),
				body.c_str()
			    );
#if 0
    qDebug("--- %s %s HTTP/1.1\r\n"
					"Host: %.*s\r\n"
					"Content-Length:%d\r\n"
					"%s\r\n"
					"%s",
					getHttpMethod(),path.c_str(),
					host.len,host.ptr,
					body.length(),
					extra_headers.c_str(),
					body.c_str()
				    );
#endif
}

// 接受http返回数据
void CHttpClient::recvReuireData(struct mg_connection *c, int ev, void *ev_data)
{
	struct mg_http_message *hm = (struct mg_http_message *) ev_data;
	m_sReqData.code = atoi(hm->uri.ptr);
	m_sReqData.status = true;
	if(m_sMethod == GET && !m_sFilePath.empty() && m_sReqData.code== 200)
	{
		FILE *fp = fopen(m_sFilePath.c_str(), "wb");
		if(fp == NULL){
			m_sReqData.status = false;
			return;
		}
		fwrite(hm->body.ptr, 1, hm->body.len, fp);
        fflush(fp);
		fclose(fp);
	}

	m_sReqData.msg = string(hm->body.ptr,hm->body.len).c_str();
}

// 接受块数据
void CHttpClient::recvReuireChunkData(struct mg_connection *c, int ev, void *ev_data)
{
	struct mg_http_message *hm = (struct mg_http_message *) ev_data;
}

// 清除http请求
void CHttpClient::cancelReuire()
{
	m_bRunStatus = false;
}

// 请求回调
void CHttpClient::__httpClientCB(struct mg_connection *c, int ev, void *ev_data)
{
	if (ev == MG_EV_CONNECT) {
		m_sCon = c;
		m_lPrev_second = 0;
		c->label[0] = 'X';
		if (mg_url_is_ssl(m_sUrl.c_str())) {
			 struct mg_str host = mg_url_host(m_sUrl.c_str());
			 if(m_sTlsOpts.srvname.len == 0){
				 m_sTlsOpts.srvname = host;
			 }
			 mg_tls_init(c, &m_sTlsOpts);
		 }
		sendReuireData(c,ev,ev_data);
    } else if (ev == MG_EV_HTTP_MSG) {
    	recvReuireData(c,ev,ev_data);
	    c->is_closing = 1;
	    m_bRunStatus = false;
	}else if (ev == MG_EV_HTTP_CHUNK) {
    	recvReuireChunkData(c,ev,ev_data);
	} else if (ev == MG_EV_ERROR) {
        qDebug("MG_EV_ERROR:%s",ev_data);
		m_sCon = NULL;
	    if(m_iConTimeTmp >= m_iConTime){
	    	m_bRunStatus = false;
	    }
    } else if (ev == MG_EV_POLL && c->label[0] == 'X') {
    	unsigned long second = (*(unsigned long *) ev_data) / 1000;
    	if(m_lPrev_second == 0) {
    		m_lPrev_second = second;
    	}
    	if(abs((long)(second - m_lPrev_second)) >= m_iRecvTime){
			c->is_closing = 1;
			m_bRunStatus = false;
            qDebug("m_iRecvTime");
    	}

    } else if(ev == MG_EV_READ) {
    	m_lPrev_second = 0;
    } else if(ev == MG_EV_CLOSE) {
    	m_bRunStatus = false;
    	m_sCon= NULL;
    } else if(ev == MG_EV_OPEN) {

    }

}


// 定时器
void CHttpClient::__httpConTimeOutCB()
{
	if(m_iConTimeTmp < m_iConTime){
		// 连接超时
		m_iConTimeTmp += 3;
		if(m_sCon == NULL){
			mg_http_connect(&m_sMgr, m_sUrl.c_str(), httpClientCB, this);
		}
	}else {
		if(m_sCon == NULL){
			m_bRunStatus = false;
		}
	}
}

// 执行请求
void CHttpClient::requireRun() {

	struct mg_timer time;;
	int topts = MG_TIMER_REPEAT | MG_TIMER_RUN_NOW;
	mg_timer_init(&m_sMgr,&time, 3000, topts, httpConTimeOutCB, this);
	m_bRunStatus = true;
	while(m_bRunStatus){
		mg_mgr_poll(&m_sMgr, 1000);
	}
	mg_timer_free(&m_sMgr,&time);
}

tsReqData CHttpClient::startReuire()
{
	requireRun();
	return m_sReqData;
}


// 初始化连接参数
void CHttpClient::initContParm(tsHttpMethod method,const char *url,int conTimeout,int recvTimeout)
{
	m_sMethod = method;
	m_sUrl = url;
	m_iConTime = conTimeout;
	m_iRecvTime = recvTimeout;
}

// 初始化数据参数
void CHttpClient::initDataParm(MAP_PARAMS *parm,MAP_PARAMS *extra_headers,string data,string fileName,string filePath)
{
	// 请求头
	m_pHeadParams = extra_headers;
	m_pParms = parm;
	m_tBody = data;
	m_sFileName = fileName;
	m_sFilePath = filePath;
}

// 初始化tls
void CHttpClient::initTls(struct mg_tls_opts *sTlsOpts)
{
	if(sTlsOpts){
		memcpy(&m_sTlsOpts,sTlsOpts,sizeof(struct mg_tls_opts));
	}
}

// 下载文件
tsReqData CHttpClient::downFile(const char *url,const char *savePath, int conTimeout,int recvTimeout )
{
	CHttpClient clinet;
	clinet.initContParm(GET,url, conTimeout,recvTimeout);
	clinet.initDataParm(NULL,NULL,"","",savePath);
	return clinet.startReuire();
}
// 上传文件
tsReqData CHttpClient::pullFile(tsHttpMethod method,const char *url,MAP_PARAMS *parms,MAP_PARAMS *headParams,string fileName,string filePath,int conTimeout,int recvTimeout)
{
	CHttpClient clinet;
	clinet.initContParm(method,url,conTimeout,recvTimeout);
	clinet.initDataParm(parms,headParams,"",fileName,filePath);
	return clinet.startReuire();

}

tsReqData CHttpClient::httpReq(tsHttpMethod method,const char *url,MAP_PARAMS *parms,MAP_PARAMS *headParams,string data,int conTimeout,int recvTimeout)
{
	CHttpClient clinet;
	clinet.initContParm(method,url,conTimeout,recvTimeout);
	clinet.initDataParm(parms,headParams,data,"","");
	return clinet.startReuire();

}



bool serverIsRun = false;
string __url;

static void OnHttpServerConnectEvent(mg_connection *connection, int event_type, void *event_data, void* pUserData)
{
	bool *p = (bool*)pUserData;
	int connect_status;
	switch (event_type)
	{
		case MG_EV_CONNECT:
		{
			*p = true;
			connection->is_closing = 1;
			serverIsRun = false;
			break;
		}
		case MG_EV_HTTP_CHUNK:
		case MG_EV_HTTP_MSG:
		case MG_EV_READ:
		case MG_EV_CLOSE:
		{
			connection->is_closing = 1;
			serverIsRun = false;
			break;
		}
		default:
			break;
	}
}


// 连接超时
static void httpConTimeOutCBCon(void *arg)
{

	serverIsRun = false;
    qDebug("connect[%s] time out", __url.c_str());

}
/**
*  @brief http连接请求 (只测试服务器能否连接)
*  @param[IN] strUrl url地址
*  @param[IN] connectTimeout 连接超时
*  return 0 成功
*/

bool CHttpClient::getServerIsSuccess(const std::string &strUrl, int connectTimeout)
{
#if 1
	CGetHostByName::initMongoose();
	struct mg_mgr mgr;
	mg_mgr_init(&mgr);
	bool isConnect = false;
	serverIsRun = true;
	__url = strUrl;
	// 连接服务器
	auto connection = mg_http_connect(&mgr, strUrl.c_str(), OnHttpServerConnectEvent, &isConnect);
	if(connection == NULL){
		mg_mgr_free(&mgr);
		return false;
	}

	struct mg_timer time;;
	int topts = MG_TIMER_REPEAT;
	mg_timer_init(&mgr,&time, connectTimeout, topts, httpConTimeOutCBCon, NULL);

	while (serverIsRun)
	{
		mg_mgr_poll(&mgr, 1000);
	}
	mg_mgr_free(&mgr);
	mg_timer_free(&mgr,&time);
	return isConnect;
#else
	struct sockaddr_in m_dest_addr;
	unsigned long inaddr = 0l;
	bool ret = false;
	int fd;

	// 获取目标地址
	bzero(&m_dest_addr, sizeof (m_dest_addr));
    m_dest_addr.sin_family = AF_INET;
	if (inaddr = inet_addr(strUrl.c_str()) == INADDR_NONE)
    {
		CGetHostByName gethost;
		string ip_qg = gethost.getHostName(strUrl);
		if(ip_qg.empty()){
			return false;
		}
		if(inaddr = inet_addr(ip_qg.c_str()) == INADDR_NONE){
            qDebug("gethostbyname error \r\n");
			return false;
		}
    }
    m_dest_addr.sin_addr.s_addr = inet_addr(strUrl.c_str());


    int sock;
    char message[30];
    int str_len = 0;
    int idx = 0, read_len = 0;

    //1.socket
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1)
        qDebug("socket() error");

    //2.connect，连接对方地址
    if (connect(sock, (struct sockaddr *)&m_dest_addr, sizeof(m_dest_addr)) == -1){
        qDebug("connect() error!");
        close(sock);
    	return false;
    }
    close(sock);
    return true;
#endif
}

BASENET_END_NAMESPACE
